package gang.org.springframework.boot.autoconfigure;

import gang.org.springframework.boot.context.annotation.GangImportCandidates;
import gang.org.springframework.framework.factory.GangBeanClassLoaderAware;
import gang.org.springframework.framework.metadata.GangAnnotationAttributes;
import gang.org.springframework.framework.metadata.GangAnnotationMetadata;
import gang.org.springframework.framework.support.GangSpringFactoriesLoader;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 业务意义
 * 1，GangAutoConfigurationImportSelector 元注解直接引入的类
 * 2，AutoConfigurationGroup 内部类，真正解析spring.factories文件实现Class
 *
 * @author gang.chen
 * @description 工厂配置文件加载
 * @Date 2021/6/20 14:24
 */
public class GangAutoConfigurationImportSelector implements GangDeferredImportSelector, GangBeanClassLoaderAware {

    private ClassLoader classLoader;

    //TODO
    protected GangAutoConfigurationEntry getAutoConfigurationEntry(GangAnnotationMetadata metadata)
    {
        GangAnnotationAttributes attributes = null;
        List<String> configurations = getCandidateConfigurations(metadata, attributes);

        return new GangAutoConfigurationEntry(configurations,null);
    }

    //TODO
    protected List<String> getCandidateConfigurations(GangAnnotationMetadata metadata, GangAnnotationAttributes attributes){

        List<String> configurations = GangSpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());

        //TODO
        GangImportCandidates.load(GangAutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);

        //TODO
        return configurations;
    }

    //TODO
    protected Class<?> getSpringFactoriesLoaderFactoryClass(){
        return GEnableAutoConfiguration.class;
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    public ClassLoader getBeanClassLoader(){
        return this.classLoader;
    }


    public static class AutoConfigurationGroup implements GangDeferredImportSelector.Group{

        private final Map<String, GangAnnotationMetadata> entries = new LinkedHashMap<>();

        private final List<GangAutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();

        /**
         * 业务意义：spring.factories自动配置类从配置文件中解析出来，不做其它处理
         * @param metadata {@link gang.org.springframework.framework.metadata.GangStandardAnnotationMetadata}
         * @param selector {@link GangAutoConfigurationImportSelector}
         *
         * */
        @Override
        public void process(GangAnnotationMetadata metadata, GangDeferredImportSelector selector) {
            System.out.println("GangAutoConfigurationImportSelector->process->自动装配开始...");
            //TODO
            GangAutoConfigurationEntry autoConfigurationEntry = ((GangAutoConfigurationImportSelector) selector).getAutoConfigurationEntry(metadata);

            this.autoConfigurationEntries.add(autoConfigurationEntry);

            //TODO
            for (String importClassName : autoConfigurationEntry.getConfigurations()) {
                this.entries.putIfAbsent(importClassName, metadata);
            }
        }

        /**
         * Entry封装
         * 1）importClassName 类名全路径
         * 2）metadata 启动主类 SpringbootManualApplication元数据
         * * */
        @Override
        public Iterable<Entry> selectImport() {
            List<GangAutoConfigurationEntry> autoConfigurationEntries = this.autoConfigurationEntries;
            Set<String> processedConfigurations = autoConfigurationEntries.stream().map(GangAutoConfigurationEntry::getConfigurations).flatMap(Collection::stream).collect(Collectors.toCollection(LinkedHashSet::new));
            return processedConfigurations.stream().map((importClassName) -> new Entry(importClassName,this.entries.get(importClassName))).collect(Collectors.toList());
        }
    }

    @Override
    public Class<? extends Group> getImportGroup() {
        return AutoConfigurationGroup.class;
    }

    protected static class GangAutoConfigurationEntry{
        private final List<String> configurations;
        private final Set<String> exclusions;

        public GangAutoConfigurationEntry(List<String> configurations, Set<String> exclusions) {
            this.configurations = configurations;
            this.exclusions = exclusions;
        }

        public List<String> getConfigurations() {
            return this.configurations;
        }

        public Set<String> getExclusions() {
            return this.exclusions;
        }
    }
}

